package com.ruoyi.flowable.service.impl;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.flowable.common.constant.ProcessConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.flowable.common.enums.FlowComment;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.flowable.domain.dto.*;
import com.ruoyi.flowable.domain.vo.FlowQueryVo;
import com.ruoyi.flowable.domain.vo.FlowTaskVo;
import com.ruoyi.flowable.factory.FlowServiceFactory;
import com.ruoyi.flowable.flow.CustomProcessDiagramGenerator;
import com.ruoyi.flowable.flow.FindNextNodeUtil;
import com.ruoyi.flowable.flow.FlowableUtils;
import com.ruoyi.flowable.service.IFlowTaskService;
import com.ruoyi.flowable.service.ISysDeployFormService;
import com.ruoyi.flowable.service.ISysFormService;
import com.ruoyi.system.domain.SysCopy;
import com.ruoyi.system.domain.SysForm;
import com.ruoyi.system.service.ISysCopyService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd;
import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Tony
 * @date 2021-04-03
 **/
@Service
@Slf4j
public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService {

    @Resource
    private ISysUserService sysUserService;
    @Resource
    private ISysRoleService sysRoleService;
    @Resource
    private ISysDeployFormService sysDeployFormService;
    @Resource
    private ISysFormService sysFormService;
    @Resource
    private ISysCopyService sysCopyService;

    /**
     * 审批任务
     *
     * @param flowTaskVo 请求实体参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public AjaxResult complete(FlowTaskVo flowTaskVo) {
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            return AjaxResult.error("流程任务不存在");
        }
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            // 归还委托任务
            taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getProcInsId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment());
            taskService.resolveTask(flowTaskVo.getTaskId());
        } else {
            // 审批任务
            taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getProcInsId(), FlowComment.NORMAL.getType(), flowTaskVo.getComment());
            taskService.setAssignee(flowTaskVo.getTaskId(), SecurityUtils.getLoginUser().getUserId().toString());
            taskService.complete(flowTaskVo.getTaskId(), flowTaskVo.getVariables());
        }
        // 任务抄送人发送消息
        sendCopyTask(flowTaskVo, task);
        return AjaxResult.success();
    }

    /**
     * 重新申请任务
     *
     * @param flowTaskVo 请求实体参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public AjaxResult refillTask(FlowTaskVo flowTaskVo) {
        // 根据任务ID查询任务
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        // 如果任务不存在，返回错误信息
        if (Objects.isNull(task)) {
            return AjaxResult.error("流程任务不存在");
        }
        // 如果任务处于委托状态，归还委托任务
        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            // 添加委托任务的处理意见
            taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getProcInsId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment());
            // 归还委托任务
            taskService.resolveTask(flowTaskVo.getTaskId());
        } else {
            // 如果任务处于审批状态
            if (task.getDelegationState() == null) {
                // 发起人任务设置表单key信息 ,后续退回/驳回任务时可以根据该字段初始化表单
                if (StringUtils.isBlank(task.getFormKey())) {
                    // 获取当前流程实例
                    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                            .processInstanceId(task.getProcessInstanceId())
                            .singleResult();
                    if (Objects.nonNull(processInstance)) {
                        // 获取流程表单信息
                        SysForm sysForm = sysDeployFormService.selectSysDeployFormByDeployId(processInstance.getDeploymentId());
                        if (Objects.nonNull(sysForm)) {
                            // 设置任务的表单key信息
                            task.setFormKey(sysForm.getFormId().toString());
                            taskService.saveTask(task);
                        }
                    }
                }
                // 添加任务的处理意见
                taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getProcInsId(), FlowComment.SUBMIT.getType(), flowTaskVo.getComment());
                // 设置任务的处理人为流程发起人
                taskService.setAssignee(flowTaskVo.getTaskId(), ProcessConstants.PROCESS_INITIATOR);
                // 完成任务
                taskService.complete(flowTaskVo.getTaskId(), flowTaskVo.getVariables());
            }
        }
        // 向任务的抄送人发送消息
        sendCopyTask(flowTaskVo, task);
        // 返回成功信息
        return AjaxResult.success();
    }

    /**
     * 驳回任务
     *
     * @param flowTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rejectTask(FlowTaskVo flowTaskVo) {
        // 当前任务 task
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        if (task.isSuspended()) {
            throw new CustomException("任务处于挂起状态!");
        }
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    // 获取节点信息
                    source = flowElement;
                }
            }
        }

        // 目的获取所有跳转到的节点 targetIds
        // 获取当前节点的所有父级用户任务节点
        // 深度优先算法思想：延边迭代深入
        List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
        if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
            throw new CustomException("驳回任务失败,驳回为任务开始节点!");
        }
        // 获取活动 ID 即节点 Key
        List<String> parentUserTaskKeyList = new ArrayList<>();
        parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
        // 获取全部历史节点活动实例，即已经走过的节点历史，数据采用开始时间升序
        List<HistoricTaskInstance> historicTaskInstanceList = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByHistoricTaskInstanceStartTime()
                .asc()
                .list();

        // 数据清洗，将回滚导致的脏数据清洗掉
        List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
        // 此时历史任务实例为倒序，获取最后走的节点
        List<String> targetIds = new ArrayList<>();
        List<String> targetTaskIdList = new ArrayList<>();
        // 循环结束标识，遇到当前目标节点的次数
        int number = 0;
        List<HistoricTaskInstance> hisCompleteTaskList = new ArrayList<>();
        StringBuilder parentHistoricTaskKey = new StringBuilder();
        for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
            // 当会签时候会出现特殊的，连续都是同一个节点历史数据的情况，这种时候跳过
            if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
                continue;
            }
            parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
            if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
                number++;
            }
            // 在数据清洗后，历史节点就是唯一一条从起始到当前节点的历史记录，理论上每个点只会出现一次
            // 在流程中如果出现循环，那么每次循环中间的点也只会出现一次，再出现就是下次循环
            // number == 1，第一次遇到当前节点
            // number == 2，第二次遇到，代表最后一次的循环范围
            if (number == 2) {
                break;
            }
            // 如果当前历史节点，属于父级的节点，说明最后一次经过了这个点，需要退回这个点
            if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
                targetIds.add(historicTaskInstanceKey);

                // 添加目标节点任务编号
                Optional<HistoricTaskInstance> instance = historicTaskInstanceList.stream().filter(hisTask -> historicTaskInstanceKey.equals(hisTask.getTaskDefinitionKey())).findFirst();
                if (instance.isPresent()) {
                    HistoricTaskInstance historicTaskInstance = instance.get();
                    targetTaskIdList.add(historicTaskInstance.getId());
                    // TODO 查找已办任务节点的办理人,退回节点时,直接设置任务签收人.
                    hisCompleteTaskList.add(historicTaskInstance);
                }

            }
        }

        // 目的获取所有需要被跳转的节点 currentIds
        // 取其中一个父级任务，因为后续要么存在公共网关，要么就是串行公共线路
        UserTask oneUserTask = parentUserTaskList.get(0);
        // 获取所有正常进行的任务节点 Key，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runTaskKeyList = new ArrayList<>();
        runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
        // 需驳回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runTaskList 比对，获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
        currentUserTaskList.forEach(item -> currentIds.add(item.getId()));


        // 规定：并行网关之前节点必须需存在唯一用户任务节点，如果出现多个任务节点，则并行网关节点默认为结束节点，原因为不考虑多对多情况
        if (targetIds.size() > 1 && currentIds.size() > 1) {
            throw new CustomException("任务出现多对多情况，无法撤回");
        }

        // 循环获取那些需要被撤回的节点的ID，用来设置驳回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
            if (currentId.equals(runTask.getTaskDefinitionKey())) {
                currentTaskIds.add(runTask.getId());
            }
        }));
        // 设置驳回意见
        currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment()));
        // 处理流程变量中历史表单数据
        handelRenewFormInfo(targetTaskIdList, process);
        try {
            // 如果父级任务多于 1 个，说明当前节点不是并行节点，原因为不考虑多对多情况
            if (targetIds.size() > 1) {
                // 1 对 多任务跳转，currentIds 当前节点(1)，targetIds 跳转到的节点(多)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds)
                        .changeState();
                handelMultipleRefillFLowElement(process, targetIds, hisCompleteTaskList, task.getProcessInstanceId());
            }
            // 如果父级任务只有一个，因此当前任务可能为网关中的任务
            if (targetIds.size() == 1) {
                // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetIds.get(0) 跳转到的节点(1)
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0))
                        .changeState();
                handelSingleRefillFLowElement(process, targetIds, hisCompleteTaskList, task.getProcessInstanceId());
            }
        } catch (FlowableObjectNotFoundException e) {
            throw new CustomException("未找到流程实例，流程可能已发生变化");
        } catch (FlowableException e) {
            throw new CustomException("无法取消或开始活动");
        }
    }

    /**
     * 处理流程变量中历史表单数据
     * 如果被驳回的节点上存在表单信息 , 需要清空表单后, 处理任务时重新填写
     * {@link com.ruoyi.flowable.service.impl.FlowDefinitionServiceImpl#startProcessInstanceById(com.ruoyi.flowable.domain.vo.FlowTaskVo)}
     *
     * @param targetTaskIdList 需要退回/驳回任务ID
     * @param process          流程模型
     */
    private void handelRenewFormInfo(List<String> targetTaskIdList, Process process) {
        targetTaskIdList.forEach(targetTaskId -> {
            // 获取历史任务信息
            HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery()
                    .includeProcessVariables()
                    .finished()
                    .taskId(targetTaskId)
                    .singleResult();
            if (Objects.nonNull(historicTask) && StringUtils.isNotBlank(historicTask.getFormKey())) {
                // 退回,驳回到任务申请人节点时
                if (ProcessConstants.PROCESS_INITIATOR.equals(historicTask.getAssignee())) {
                    /*
                     * 如果退回到开始节点时,那么需要获取所有任务节点上的表单信息,然后从流程变量中删除.
                     * 比如: a,b,c三个节点, c->a时,则需要同时删除 a,b两个任务节点填写过的表单信息
                     *
                     * 通过任务执行ID 和流程实例ID 可以获取到当前被退回的所有任务节点信息,依次读取节点上的表单信息后从流程变量中进行删除
                     */
                    List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                            .processInstanceId(historicTask.getProcessInstanceId())
                            .executionId(historicTask.getExecutionId())
                            .finished()
                            .list();
                    if (CollectionUtils.isNotEmpty(historicTaskInstances)) {
                        historicTaskInstances.forEach(historicTaskInstance -> {
                            if (Objects.nonNull(historicTaskInstance) && StringUtils.isNotBlank(historicTaskInstance.getFormKey())) {
                                // 获取任务节点上表单信息
                                SysForm sysForm = sysFormService.selectSysFormById(Long.parseLong(historicTaskInstance.getFormKey()));
                                if (Objects.nonNull(sysForm)) {
                                    FlowFormFieldDto formFieldDto = JSONObject.parseObject(sysForm.getFormContent(), FlowFormFieldDto.class);
                                    List<FlowFormFieldDto.WidgetList> widgetList = formFieldDto.getWidgetList();
                                    // 获取需要删除的表单字段
                                    widgetList.forEach(widget -> runtimeService.removeVariable(historicTaskInstance.getProcessInstanceId(), widget.getOptions().getName()));
                                }
                            }
                        });
                    }
                    // TODO 处理发起人节点后的下一任务审批人
                    handleRefillApproval(historicTask.getProcessVariables(), historicTask, process);
                } else {
                    /*
                     * 删除子表单数据. 比如 a,b,c 三个节点, c->b时,则需要删除b任务处理填写的表单数据
                     */
                    // 获取任务节点上表单信息
                    SysForm sysForm = sysFormService.selectSysFormById(Long.parseLong(historicTask.getFormKey()));
                    if (Objects.nonNull(sysForm)) {
                        FlowFormFieldDto formFieldDto = JSONObject.parseObject(sysForm.getFormContent(), FlowFormFieldDto.class);
                        List<FlowFormFieldDto.WidgetList> widgetList = formFieldDto.getWidgetList();
                        // 获取需要删除的表单字段
                        List<String> variableNames = widgetList.stream().map(widget -> widget.getOptions().getName()).collect(Collectors.toList());
                        // 通过流程实例编号删除流程变量表单字段 getProcessInstanceId == act_his_varinst 中的 executionId
                        runtimeService.removeVariables(historicTask.getProcessInstanceId(), variableNames);

                        // 获取流程变量(已完成的表单信息存储在流程变量里面的)
                        Map<String, Object> variables = historicTask.getProcessVariables();
                        // 历史表单数据
                        Object formJson = variables.get("formJson");
                        JSONObject formData = JSONObject.parseObject(JSON.toJSONString(formJson));
                        // 表单json渲染数据重新赋值
                        List<JSONObject> oldFields = JSON.parseObject(JSON.toJSONString(formData.get("widgetList")), new TypeReference<List<JSONObject>>() {
                        });
                        oldFields.removeIf(widget ->
                                variableNames.contains(JSONObject.parseObject(JSON.toJSONString(widget.getJSONObject("options")), FlowFormFieldDto.Options.class).getName())
                        );

                        formData.put("widgetList", oldFields);
                        runtimeService.setVariable(historicTask.getProcessInstanceId(), "formJson", formData);
                    }
                }
            }
        });
    }

    /**
     * 处理发起人节点后的下一任务审批人
     * TODO: 暂时不处理下一节点是会签任务情况,流程设计时,先避免这种情况
     * 被退回/驳回任务后,申请人重新发起不需要选择任务接收人
     * 单独设置下一步任务处理人的变量，重新申请任务时，可以读取这个值，赋值给流程任务接收人
     *
     * @param processVariables 流程变量
     * @param historicTask     流程任务
     * @param process          流程模型
     */
    private void handleRefillApproval(Map<String, Object> processVariables, HistoricTaskInstance historicTask, Process process) {
        // 当前任务退回/驳回人赋值给指定的流程变量,重新申请任务时就可以直接进行提交不用再次选择审批人
        // TODO : 如果是会签任务,则只能设置发起任务时选择的多个人员,不能直接指定某个人进行处理
        FlowElement flowElement = process.getFlowElement(historicTask.getTaskDefinitionKey());
        if (flowElement instanceof UserTask) {
            UserTask userTask = (UserTask) flowElement;
            MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics();
            // 校验会签节点
            if (Objects.nonNull(multiInstance)) {
                runtimeService.setVariable(historicTask.getProcessInstanceId(), ProcessConstants.PROCESS_APPROVAL, processVariables.get(ProcessConstants.PROCESS_REFILL_APPROVAL));
            } else {
                runtimeService.setVariable(historicTask.getProcessInstanceId(), ProcessConstants.PROCESS_APPROVAL, SecurityUtils.getLoginUser().getUserId().toString());
            }
        }
    }

    /**
     * 回退任务
     *
     * @param flowTaskVo 请求实体参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void returnTask(FlowTaskVo flowTaskVo) {
        // 当前任务 task
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        if (task.isSuspended()) {
            throw new CustomException("任务处于挂起状态!");
        }
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        // 获取跳转的节点元素
        FlowElement target = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 当前任务节点元素
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = flowElement;
                }
                // 跳转的节点元素
                if (flowElement.getId().equals(flowTaskVo.getTargetKey())) {
                    target = flowElement;
                }
            }
        }

        // 从当前节点向前扫描
        // 如果存在路线上不存在目标节点，说明目标节点是在网关上或非同一路线上，不可跳转
        // 否则目标节点相对于当前节点，属于串行
        Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null);
        if (!isSequential) {
            throw new CustomException("当前节点相对于目标节点，不属于串行关系，无法回退");
        }

        // 获取所有正常进行的任务节点 Key，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runTaskKeyList = new ArrayList<>();
        runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
        // 需退回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runTaskList 比对，获取需要撤回的任务
        List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null);
        currentUserTaskList.forEach(item -> currentIds.add(item.getId()));

        // 循环获取那些需要被撤回的节点的ID，用来设置退回原因
        List<String> currentTaskIds = new ArrayList<>();
        currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
            if (currentId.equals(runTask.getTaskDefinitionKey())) {
                currentTaskIds.add(runTask.getId());
            }
        }));
        // 设置回退意见
        currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment()));
        // 同过流程执行ID,任务执行ID,需要退回任务节点key 获取到历史任务
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .executionId(task.getExecutionId())
                .taskDefinitionKey(flowTaskVo.getTargetKey())
                .finished()
                .list();
        if (CollectionUtils.isNotEmpty(historicTaskInstances)) {
            List<String> targetTaskIdList = new ArrayList<>();
            // 添加需要退回的目标节点任务编号(单个任务节点)
            historicTaskInstances.forEach(item -> targetTaskIdList.add(item.getId()));
            // 处理流程变量中历史表单数据
            handelRenewFormInfo(targetTaskIdList, process);
        }
        try {
            // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetKey 跳转到的节点(1)
            runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(task.getProcessInstanceId())
                    .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState();
            handelSingleRefillFLowElement(process, Collections.singletonList(flowTaskVo.getTargetKey()), historicTaskInstances, task.getProcessInstanceId());
        } catch (FlowableObjectNotFoundException e) {
            throw new CustomException("未找到流程实例，流程可能已发生变化");
        } catch (FlowableException e) {
            throw new CustomException("无法取消或开始活动");
        }
    }


    /**
     * 处理流程申请人节点
     * 如果驳回的是开节点,则通过流程实例ID 查找当前待办的任务 赋值任务接收人为 INITIATOR
     * <p>
     * TODO 这个类实现方式待优化
     *
     * @param process             流程模型
     * @param targetIds           退回目标流程节点ID
     * @param procInsId           流程实例ID
     * @param hisCompleteTaskList 历史完任务列表
     */
    private void handelMultipleRefillFLowElement(Process process, List<String> targetIds, List<HistoricTaskInstance> hisCompleteTaskList, String procInsId) {
        // 查找并验证当前流程是否设置了被退回/驳回后可以重新申请任务
        String refill = process.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_REFILL);
        if (StringUtils.isNotBlank(refill) && "true".equals(refill)) {
            // 找到开始节点
            process.getFlowElements().stream()
                    .filter(flowElement -> flowElement instanceof StartEvent)
                    .findFirst()
                    .ifPresent(startEvent -> {
                        List<SequenceFlow> sequenceFlows = ((StartEvent) startEvent).getOutgoingFlows();
                        // 获取出口连线, 此时从开始节点往后,只能是一个出口,那么直接从申请人任务节点开始获取下一步任务
                        if (!sequenceFlows.isEmpty()) {
                            SequenceFlow sequenceFlow = sequenceFlows.get(0);
                            FlowElement flowElement = sequenceFlow.getTargetFlowElement();
                            if (Objects.nonNull(flowElement)) {
                                // 从被退回/驳回的节点中查找该节点是否是任务申请人节点
                                boolean present = targetIds.stream().anyMatch(targetId -> targetId.equals(flowElement.getId()));
                                List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInsId).list();
                                if (present) {
                                    tasks.stream()
                                            .filter(t -> flowElement.getId().equals(t.getTaskDefinitionKey()))
                                            .findFirst()
                                            .ifPresent(task -> taskService.setAssignee(task.getId(), ProcessConstants.PROCESS_INITIATOR));
                                } else {
                                    // 直接把原任务的处理人赋值给新任务的处理人,避免多人签收的任务被驳回后,还需要重新签收
                                    // 通过流程key 去查找原任务处理人
                                    tasks.forEach(userTask -> hisCompleteTaskList.stream()
                                            .filter(completeTask -> completeTask.getTaskDefinitionKey().equals(userTask.getTaskDefinitionKey()))
                                            .findFirst()
                                            .ifPresent(task -> taskService.setAssignee(userTask.getId(), task.getAssignee())));
                                }
                            }
                        }
                    });
        } else {
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInsId).list();
            // 直接把原任务的处理人赋值给新任务的处理人,避免多人签收的任务被驳回后,还需要重新签收
            // 通过流程key 去查找原任务处理人
            tasks.forEach(userTask -> hisCompleteTaskList.stream()
                    .filter(completeTask -> completeTask.getTaskDefinitionKey().equals(userTask.getTaskDefinitionKey()))
                    .findFirst()
                    .ifPresent(task -> taskService.setAssignee(userTask.getId(), task.getAssignee())));
        }
    }

    /**
     * 处理流程申请人节点
     * 如果驳回的是开节点,则通过流程实例ID 查找当前待办的任务 赋值任务接收人为 INITIATOR
     * TODO 这个类实现方式待优化
     *
     * @param process             流程模型
     * @param targetIds           退回目标流程节点ID
     * @param procInsId           流程实例ID
     * @param hisCompleteTaskList 历史完任务列表
     */
    private void handelSingleRefillFLowElement(Process process, List<String> targetIds, List<HistoricTaskInstance> hisCompleteTaskList, String procInsId) {
        // 查找并验证当前流程是否设置了被退回/驳回后可以重新申请任务
        String refill = process.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_REFILL);
        if (StringUtils.isNotBlank(refill) && "true".equals(refill)) {
            // 找到开始节点
            process.getFlowElements().stream()
                    .filter(flowElement -> flowElement instanceof StartEvent)
                    .findFirst()
                    .ifPresent(startEvent -> {
                        List<SequenceFlow> sequenceFlows = ((StartEvent) startEvent).getOutgoingFlows();
                        // 获取出口连线, 此时从开始节点往后,只能是一个出口,那么直接从申请人任务节点开始获取下一步任务
                        if (!sequenceFlows.isEmpty()) {
                            SequenceFlow sequenceFlow = sequenceFlows.get(0);
                            FlowElement flowElement = sequenceFlow.getTargetFlowElement();
                            if (Objects.nonNull(flowElement)) {
                                // 从被退回/驳回的节点中查找该节点是否是任务申请人节点
                                boolean present = targetIds.stream().anyMatch(targetId -> targetId.equals(flowElement.getId()));
                                Task userTask = taskService.createTaskQuery().processInstanceId(procInsId).singleResult();
                                if (Objects.nonNull(userTask)) {
                                    if (present) {
                                        // 被退回/驳回的节点是任务申请人节点时,设置固定的值 INITIATOR
                                        taskService.setAssignee(userTask.getId(), ProcessConstants.PROCESS_INITIATOR);
                                    } else {
                                        hisCompleteTaskList.stream()
                                                .filter(completeTask -> completeTask.getTaskDefinitionKey().equals(userTask.getTaskDefinitionKey()))
                                                .findFirst()
                                                .ifPresent(task -> taskService.setAssignee(userTask.getId(), task.getAssignee()));
                                    }
                                }
                            }
                        }
                    });
        } else {
            // 直接把原任务的处理人赋值给新任务的处理人,避免多人签收的任务被驳回后,还需要重新签收
            // 通过流程key 去查找原任务处理人
            Task userTask = taskService.createTaskQuery().processInstanceId(procInsId).singleResult();
            hisCompleteTaskList.stream()
                    .filter(completeTask -> completeTask.getTaskDefinitionKey().equals(userTask.getTaskDefinitionKey()))
                    .findFirst()
                    .ifPresent(task -> taskService.setAssignee(userTask.getId(), task.getAssignee()));
        }

    }


    /**
     * 获取所有可回退的节点
     *
     * @param flowTaskVo
     * @return
     */
    @Override
    public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) {
        // 当前任务 task
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息，暂不考虑子流程情况
        Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        // 获取当前任务节点元素
        UserTask source = null;
        if (flowElements != null) {
            for (FlowElement flowElement : flowElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = (UserTask) flowElement;
                }
            }
        }
        // 获取节点的所有路线
        List<List<UserTask>> roads = FlowableUtils.findRoad(source, null, null, null);
        // 可回退的节点列表
        List<UserTask> userTaskList = new ArrayList<>();
        for (List<UserTask> road : roads) {
            if (userTaskList.size() == 0) {
                // 还没有可回退节点直接添加
                userTaskList = road;
            } else {
                // 如果已有回退节点，则比对取交集部分
                userTaskList.retainAll(road);
            }
        }
        return AjaxResult.success(userTaskList);
    }

    /**
     * 删除任务
     *
     * @param flowTaskVo 请求实体参数
     */
    @Override
    public void deleteTask(FlowTaskVo flowTaskVo) {
        // todo 待确认删除任务是物理删除任务 还是逻辑删除，让这个任务直接通过？
        taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment());
    }

    /**
     * 认领/签收任务
     * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失
     *
     * @param flowTaskVo 请求实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void claim(FlowTaskVo flowTaskVo) {
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId());
        // 任务抄送人发送消息
        sendCopyTask(flowTaskVo, task);
    }

    /**
     * 取消认领/签收任务
     *
     * @param flowTaskVo 请求实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unClaim(FlowTaskVo flowTaskVo) {
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        taskService.unclaim(flowTaskVo.getTaskId());
        // 任务抄送人发送消息
        sendCopyTask(flowTaskVo, task);
    }

    /**
     * 委派任务
     * 任务委派只是委派人将当前的任务交给被委派人进行审批，处理任务后又重新回到委派人身上。
     *
     * @param flowTaskVo 请求实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delegateTask(FlowTaskVo flowTaskVo) {
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        // 设置任务拥有者信息
        taskService.setOwner(flowTaskVo.getTaskId(), SecurityUtils.getLoginUser().getUserId().toString());
        // 设置任务委派信息
        taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getUserId());
        // 添加委派任务意见
        taskService.addComment(flowTaskVo.getTaskId(),
                flowTaskVo.getProcInsId(),
                FlowComment.DELEGATE.getType(),
                SecurityUtils.getLoginUser().getUser().getNickName() + "委托任务给" + flowTaskVo.getUserName() + ", 委托备注:" + flowTaskVo.getComment());
        // 任务抄送人发送消息
        sendCopyTask(flowTaskVo, task);
    }

    /**
     * 任务归还
     * 被委派人完成任务之后，将任务归还委派人
     *
     * @param flowTaskVo 请求实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resolveTask(FlowTaskVo flowTaskVo) {
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        taskService.resolveTask(flowTaskVo.getTaskId());
        // 任务抄送人发送消息
        sendCopyTask(flowTaskVo, task);
    }


    /**
     * 转办任务
     * 直接将办理人换成别人，这时任务的拥有者不再是转办人
     *
     * @param flowTaskVo 请求实体参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transferTask(FlowTaskVo flowTaskVo) {
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            throw new CustomException("流程任务不存在!");
        }
        // 设置任务拥有者信息
        taskService.setOwner(flowTaskVo.getTaskId(), SecurityUtils.getLoginUser().getUserId().toString());
        // 设置任务转办人
        taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getUserId());
        // 添加转办任务意见
        taskService.addComment(flowTaskVo.getTaskId(),
                flowTaskVo.getProcInsId(), FlowComment.TRANSFER.getType(),
                SecurityUtils.getLoginUser().getUser().getNickName() + "转办任务给" + flowTaskVo.getUserName() + ", 转办备注:" + flowTaskVo.getComment());
        // 任务抄送人发送消息
        sendCopyTask(flowTaskVo, task);

    }

    /**
     * 多实例加签
     * act_ru_task、act_ru_identitylink各生成一条记录
     *
     * @param flowTaskVo
     */
    @Override
    public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) {
        managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDeployId(), flowTaskVo.getProcInsId(), flowTaskVo.getVariables()));
    }

    /**
     * 多实例减签
     * act_ru_task减1、act_ru_identitylink不变
     *
     * @param flowTaskVo
     */
    @Override
    public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) {
        managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag()));
    }

    /**
     * 我发起的流程
     *
     * @param queryVo 请求参数
     * @return
     */
    @Override
    public AjaxResult myProcess(FlowQueryVo queryVo) {
        Page<FlowTaskDto> page = new Page<>();
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                .startedBy(userId.toString())
                .orderByProcessInstanceStartTime()
//                .processDefinitionCategory(queryVo.getCategory())
                .desc()
                .listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize());
        page.setTotal(historyService.createHistoricProcessInstanceQuery().startedBy(userId.toString()).count());
        List<FlowTaskDto> flowList = historicProcessInstances.stream().map(hisIns -> {
            FlowTaskDto flowTask = new FlowTaskDto();
            flowTask.setCreateTime(hisIns.getStartTime());
            flowTask.setFinishTime(hisIns.getEndTime());
            flowTask.setProcInsId(hisIns.getId());

            // 计算耗时
            if (Objects.nonNull(hisIns.getEndTime())) {
                long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime();
                flowTask.setDuration(getDate(time));
            } else {
                long time = System.currentTimeMillis() - hisIns.getStartTime().getTime();
                flowTask.setDuration(getDate(time));
            }
            // 流程定义信息
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(hisIns.getProcessDefinitionId())
                    .singleResult();
            flowTask.setDeployId(pd.getDeploymentId());
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setCategory(pd.getCategory());
            flowTask.setProcDefVersion(pd.getVersion());
            // 当前所处流程
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list();
            if (CollectionUtils.isNotEmpty(taskList)) {
                Task task = taskList.get(0);
                flowTask.setTaskId(task.getId());
                flowTask.setTaskName(task.getName());
                if (StringUtils.isNotBlank(task.getAssignee())) {
                    if (ProcessConstants.PROCESS_INITIATOR.equals(task.getAssignee())) {
                        // 流程发起时, 申请人节点固定设置了description和assignee 为 INITIATOR, 所以可以通过该字段来校验是否需要重新发起任务
                        flowTask.setRefill(true);
                    } else if (ProcessConstants.PROCESS_REVOKE.equals(task.getAssignee())) {
                        flowTask.setRevoke(true);
                    } else {
                        // 当前任务节点办理人信息
                        SysUser sysUser = sysUserService.selectUserById(Long.parseLong(task.getAssignee()));
                        if (Objects.nonNull(sysUser)) {
                            flowTask.setAssigneeId(sysUser.getUserId());
                            flowTask.setAssigneeName(sysUser.getNickName());
                            flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : "");
                        }
                    }
                }
            } else {
                List<HistoricTaskInstance> historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list();
                HistoricTaskInstance historicTask = historicTaskInstance.get(0);
                flowTask.setTaskId(historicTask.getId());
                flowTask.setTaskName(historicTask.getName());
                if (StringUtils.isNotBlank(historicTask.getAssignee())) {
                    if (ProcessConstants.PROCESS_INITIATOR.equals(historicTask.getAssignee())) {
                        // 流程发起时, 申请人节点固定设置了description和assignee 为 INITIATOR, 所以可以通过该字段来校验是否需要重新发起任务
                        flowTask.setRefill(true);
                    } else if (ProcessConstants.PROCESS_REVOKE.equals(historicTask.getAssignee())) {
                        flowTask.setRevoke(true);
                    } else {
                        // 当前任务节点办理人信息
                        SysUser sysUser = sysUserService.selectUserById(Long.parseLong(historicTask.getAssignee()));
                        if (Objects.nonNull(sysUser)) {
                            flowTask.setAssigneeId(sysUser.getUserId());
                            flowTask.setAssigneeName(sysUser.getNickName());
                            flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : "");
                        }
                    }
                }
            }
            return flowTask;
        }).collect(Collectors.toList());
        page.setRecords(flowList);
        return AjaxResult.success(page);
    }

    /**
     * 取消申请
     * 目前实现方式: 直接将当前流程变更为已完成
     *
     * @param flowTaskVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult cancelTask(FlowTaskVo flowTaskVo) {
        List<Task> task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getProcInsId()).list();
        if (CollectionUtils.isEmpty(task)) {
            throw new CustomException("流程未启动或已执行完成，取消申请失败");
        }
        // 获取当前流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(flowTaskVo.getProcInsId())
                .singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        if (Objects.nonNull(bpmnModel)) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (CollectionUtils.isNotEmpty(endNodes)) {
                // TODO 取消流程为什么要设置流程发起人?
//                SysUser loginUser = SecurityUtils.getLoginUser().getUser();
//                Authentication.setAuthenticatedUserId(loginUser.getUserId().toString());

//                taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(),
//                        StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment());
                // 获取当前流程最后一个节点
                String endId = endNodes.get(0).getId();
                List<Execution> executions = runtimeService.createExecutionQuery()
                        .parentId(processInstance.getProcessInstanceId()).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                // 变更流程为已结束状态
                runtimeService.createChangeActivityStateBuilder()
                        .moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            }
        }

        return AjaxResult.success("流程申请已取消");
    }

    /**
     * 取回重办任务(已完成任务审批,下一节点暂未处理时,可以取回重新办理)
     *
     * @param flowTaskVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revokeTask(FlowTaskVo flowTaskVo) {
        // 校验流程是否结束
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(flowTaskVo.getProcInsId())
                .active()
                .singleResult();
        if (processInstance == null) {
            throw new CustomException("流程已结束或已挂起，无法取回重办任务!");
        }
        // 历史任务
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                .taskId(flowTaskVo.getTaskId())
                .processInstanceId(flowTaskVo.getProcInsId())
                .singleResult();
        if (historicTaskInstance == null) {
            throw new CustomException("任务不存在，无法取回重办任务!");
        }

        // 获取 bpmn 模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(historicTaskInstance.getProcessDefinitionId());
        UserTask currUserTask = FlowableUtils.getUserTaskByKey(bpmnModel, historicTaskInstance.getTaskDefinitionKey());
        // 查找下一级用户任务列表
        List<UserTask> nextUserTaskList = FlowableUtils.findNextUserTasks(currUserTask);
        List<String> nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList());

        // 获取当前节点之后已完成的流程历史节点
        List<HistoricTaskInstance> finishedTaskInsList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstance.getProcessInstanceId())
                .taskCreatedAfter(historicTaskInstance.getEndTime())
                .finished()
                .list();
        finishedTaskInsList.stream().filter(finishedTaskInstance -> nextUserTaskKeys.contains(finishedTaskInstance.getTaskDefinitionKey()))
                .findFirst().ifPresent(finishedTaskInstance -> {
                    throw new CustomException("下一任务已处理，无法取回重办任务!");
                });

        // 获取所有激活的任务节点，找到需要取回的任务
        List<Task> activateTaskList = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
        List<String> revokeExecutionIds = activateTaskList.stream().filter(task -> nextUserTaskKeys.contains(task.getTaskDefinitionKey()))
                .peek(task -> {
                    // 添加取回审批信息(避免被撤回的任务出现在已办任务列表中,这里固定设置流程审批人为任务ID)
                    // 为了前端页面流转记录展示,这里可以设置固定的被取回任务的审批人字段(revoke),然后匹配该字段,特殊处理
                    taskService.setAssignee(task.getId(), ProcessConstants.PROCESS_REVOKE);
                    taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), "任务已被取回重新办理,操作人:" + SecurityUtils.getLoginUser().getUser().getNickName());
                }).map(Task::getExecutionId).collect(Collectors.toList());

        try {
            runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(processInstance.getProcessInstanceId())
                    .moveExecutionsToSingleActivityId(revokeExecutionIds, historicTaskInstance.getTaskDefinitionKey()).changeState();
            // 设置取回的任务审批人为当前操作人
            Task userTask = taskService.createTaskQuery()
                    .includeProcessVariables()
                    .processInstanceId(processInstance.getProcessInstanceId())
                    .singleResult();
            if (Objects.nonNull(userTask)) {
                taskService.setAssignee(userTask.getId(), SecurityUtils.getLoginUser().getUserId().toString());
                // 删除已填写的表单信息
                handelRevokeTaskForm(userTask);
            }
        } catch (FlowableObjectNotFoundException e) {
            e.printStackTrace();
            throw new CustomException("未找到流程实例，流程可能已发生变化!");
        } catch (FlowableException e) {
            e.printStackTrace();
            throw new CustomException("执行操作失败,无法取回重办任务!");
        }
    }

    /**
     * 删除已填写的表单信息
     *
     * @param userTask
     */
    private void handelRevokeTaskForm(Task userTask) {
        if (StringUtils.isNotBlank(userTask.getFormKey())) {
            /*
             * 删除子表单数据. 比如 a,b,c 三个节点, b<-c时,则需要删除b任务处理填写的表单数据
             */
            // 获取任务节点上表单信息
            SysForm sysForm = sysFormService.selectSysFormById(Long.parseLong(userTask.getFormKey()));
            if (Objects.nonNull(sysForm)) {
                FlowFormFieldDto formFieldDto = JSONObject.parseObject(sysForm.getFormContent(), FlowFormFieldDto.class);
                List<FlowFormFieldDto.WidgetList> widgetList = formFieldDto.getWidgetList();
                List<String> variableNames = widgetList.stream().map(widget -> widget.getOptions().getName()).collect(Collectors.toList());
                // 删除流程变量表单字段
                taskService.removeVariables(userTask.getId(), variableNames);
                // 获取流程变量(已完成的表单信息存储在流程变量里面的)
                Map<String, Object> variables = userTask.getProcessVariables();
                // 历史表单数据
                Object formJson = variables.get("formJson");
                JSONObject formData = JSONObject.parseObject(JSON.toJSONString(formJson));

                // 表单json渲染数据重新赋值
                List<JSONObject> oldFields = JSON.parseObject(JSON.toJSONString(formData.get("widgetList")), new TypeReference<List<JSONObject>>() {
                });
                oldFields.removeIf(widget ->
                        variableNames.contains(JSONObject.parseObject(JSON.toJSONString(widget.getJSONObject("options")), FlowFormFieldDto.Options.class).getName())
                );
                formData.put("widgetList", oldFields);
                taskService.setVariable(userTask.getId(), "formJson", formData);
            }
        }
    }

    /**
     * 查询待办任务列表
     *
     * @param queryVo 查询条件
     * @return 待办任务列表
     */
    public AjaxResult todoList(FlowQueryVo queryVo) {
        Page<FlowTaskDto> page = new Page<>();
        // 只查看自己的数据
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        List<String> roleIds = sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateGroupIn(roleIds)
                .taskCandidateOrAssigned(sysUser.getUserId().toString())
                .orderByTaskCreateTime().desc();

        // 传入名称查询不到数据?
        if (StringUtils.isNotBlank(queryVo.getName())) {
            taskQuery.processDefinitionNameLike(queryVo.getName());
        }
        page.setTotal(taskQuery.count());
        List<Task> taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize());
        List<FlowTaskDto> flowList = taskList.stream().map(task -> {
            FlowTaskDto flowTask = new FlowTaskDto();
            // 当前流程信息
            flowTask.setTaskId(task.getId());
            flowTask.setTaskDefKey(task.getTaskDefinitionKey());
            flowTask.setCreateTime(task.getCreateTime());
            flowTask.setProcDefId(task.getProcessDefinitionId());
            flowTask.setExecutionId(task.getExecutionId());
            flowTask.setTaskName(task.getName());
            // 流程定义信息
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(task.getProcessDefinitionId())
                    .singleResult();
            flowTask.setDeployId(pd.getDeploymentId());
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setProcInsId(task.getProcessInstanceId());

            // 流程发起人信息
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .singleResult();
            SysUser startUser = sysUserService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId()));
            flowTask.setStartUserId(startUser.getUserId().toString());
            flowTask.setStartUserName(startUser.getNickName());
            flowTask.setStartDeptName(Objects.nonNull(startUser.getDept()) ? startUser.getDept().getDeptName() : "");
            if (StringUtils.isNotBlank(task.getAssignee())) {
                if (ProcessConstants.PROCESS_INITIATOR.equals(task.getAssignee())) {
                    flowTask.setRefill(true);
                } else if (ProcessConstants.PROCESS_REVOKE.equals(task.getAssignee())) {
                    flowTask.setRevoke(true);
                } else {
                    flowTask.setAssigneeId(Long.parseLong(task.getAssignee()));
                }
            }
            // 设置任务认领时间
            flowTask.setClaimTime(task.getClaimTime());
            return flowTask;
        }).collect(Collectors.toList());

        page.setRecords(flowList);
        return AjaxResult.success(page);
    }


    /**
     * 已办任务列表
     *
     * @param queryVo 请求参数
     * @return
     */
    @Override
    public AjaxResult finishedList(FlowQueryVo queryVo) {
        Page<FlowTaskDto> page = new Page<>();
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        List<FlowTaskDto> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .taskAssignee(userId.toString())
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize())
                .stream()
                .map(histTask -> {
                    FlowTaskDto flowTask = new FlowTaskDto();
                    // 当前流程信息
                    flowTask.setTaskId(histTask.getId());
                    // 审批人员信息
                    flowTask.setCreateTime(histTask.getCreateTime());
                    flowTask.setFinishTime(histTask.getEndTime());
                    flowTask.setDuration(getDate(histTask.getDurationInMillis()));
                    flowTask.setProcDefId(histTask.getProcessDefinitionId());
                    flowTask.setTaskDefKey(histTask.getTaskDefinitionKey());
                    flowTask.setTaskName(histTask.getName());

                    // 流程定义信息
                    ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                            .processDefinitionId(histTask.getProcessDefinitionId())
                            .singleResult();
                    flowTask.setDeployId(pd.getDeploymentId());
                    flowTask.setProcDefName(pd.getName());
                    flowTask.setProcDefVersion(pd.getVersion());
                    flowTask.setProcInsId(histTask.getProcessInstanceId());
                    flowTask.setHisProcInsId(histTask.getProcessInstanceId());

                    // 流程发起人信息
                    HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                            .processInstanceId(histTask.getProcessInstanceId())
                            .singleResult();
                    SysUser startUser = sysUserService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId()));
                    flowTask.setStartUserId(startUser.getNickName());
                    flowTask.setStartUserName(startUser.getNickName());
                    flowTask.setStartDeptName(Objects.nonNull(startUser.getDept()) ? startUser.getDept().getDeptName() : "");

                    // 如果DeleteReason 存在数据,说明从该节点退回/驳回过任务,此时不允许对任务进行取回重办
                    if (StringUtils.isNotBlank(histTask.getDeleteReason())) {
                        flowTask.setRefill(true);
                    }
                    // TODO : 这里的操作是为了控制前端已办任务列表 "取回重办" 按钮的显示隐藏 ,
                    //  如果对数据响应时间有要求, 可以不进行控制,在取回重办方法中(revokeTask())做了二次校验
                    // 如果根据流程实例ID,当前节点key在历史表中查询有待办的任务,说明从该节点被退回/驳回了,此时不允许对任务进行取回重办
//                    long count = historyService.createHistoricTaskInstanceQuery()
//                            .unfinished()
//                            .taskDefinitionKey(histTask.getTaskDefinitionKey())
//                            .processInstanceId(histTask.getProcessInstanceId())
//                            .count();
//                    if (count >= 1){
//                        flowTask.setRefill(true);
//                    }
                    return flowTask;
                })
                .collect(Collectors.toList());
        page.setTotal(historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .taskAssignee(userId.toString())
                .count());
        page.setRecords(hisTaskList);
        return AjaxResult.success(page);
    }

    /**
     * 流程历史流转记录
     *
     * @param flowTaskVo 流程信息
     * @return
     */
    @Override
    public AjaxResult flowRecord(FlowTaskVo flowTaskVo) {
        Map<String, Object> map = new HashMap<>();
        if (StringUtils.isNotBlank(flowTaskVo.getProcInsId())) {
            // 查询流程任务表
            List<HistoricTaskInstance> list = historyService
                    .createHistoricTaskInstanceQuery()
                    .processInstanceId(flowTaskVo.getProcInsId())
                    .orderByHistoricTaskInstanceStartTime()
                    .desc().list();
            List<FlowTaskDto> hisFlowList = list.stream().filter(histIns -> StringUtils.isNotBlank(histIns.getId())).map(histIns -> {
                FlowTaskDto flowTask = new FlowTaskDto();
                flowTask.setTaskId(histIns.getId());
                flowTask.setTaskName(histIns.getName());
                flowTask.setCreateTime(histIns.getCreateTime());
                flowTask.setFinishTime(histIns.getEndTime());
                if (StringUtils.isNotBlank(histIns.getAssignee())) {
                    if (ProcessConstants.PROCESS_INITIATOR.equals(histIns.getAssignee())) {
                        // 流程发起时, 申请人节点固定设置了description和assignee 为 INITIATOR, 所以可以通过该字段来校验是否需要重新发起任务
                        flowTask.setRefill(true);
                    } else if (ProcessConstants.PROCESS_REVOKE.equals(histIns.getAssignee())) {
                        flowTask.setRevoke(true);
                    } else {
                        SysUser sysUser = sysUserService.selectUserById(Long.parseLong(histIns.getAssignee()));
                        if (Objects.nonNull(sysUser)) {
                            flowTask.setAssigneeId(sysUser.getUserId());
                            flowTask.setAssigneeName(sysUser.getNickName());
                            flowTask.setDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : "");
                        }
                    }
                }
                // 展示审批人员
                List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getId());
                StringBuilder stringBuilder = new StringBuilder();
                linksForTask.forEach(identityLink -> {
                    // 获选人,候选组/角色(多个)
                    if ("candidate".equals(identityLink.getType())) {
                        if (StringUtils.isNotBlank(identityLink.getUserId())) {
                            SysUser sysUser = sysUserService.selectUserById(Long.parseLong(identityLink.getUserId()));
                            stringBuilder.append(sysUser.getNickName()).append(",");
                        }
                        if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                            SysRole sysRole = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId()));
                            stringBuilder.append(sysRole.getRoleName()).append(",");
                        }
                    }
                });
                if (StringUtils.isNotBlank(stringBuilder)) {
                    flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
                }

                flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? "未办理" : getDate(histIns.getDurationInMillis()));
                // 获取意见评论内容 TODO: 先查询list ,再通过ID匹配
                List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());
                List<FlowCommentDto> commentDtoList = new ArrayList<>();
                commentList.forEach(comment -> {
                    if (histIns.getId().equals(comment.getTaskId())) {
                        FlowComment flowComment = FlowComment.getFlowCommentByType(comment.getType());
                        FlowCommentDto commentDto = FlowCommentDto.builder()
                                .type(comment.getType())
                                .content(comment.getFullMessage())
                                .remark(flowComment.getRemark())
                                .style(flowComment.getStyle())
                                .dateTime(comment.getTime())
                                .build();
                        commentDtoList.add(commentDto);
                    }
                });
                flowTask.setCommentList(commentDtoList);
                return flowTask;
            }).collect(Collectors.toList());
            map.put("flowList", hisFlowList);
        }
        // 第一次申请获取初始化表单
        if (StringUtils.isNotBlank(flowTaskVo.getDeployId())) {
            SysForm sysForm = sysDeployFormService.selectSysDeployFormByDeployId(flowTaskVo.getDeployId());
            if (Objects.isNull(sysForm)) {
                return AjaxResult.error("请先配置流程表单");
            }
            map.put("formData", JSONObject.parseObject(sysForm.getFormContent()));
        }
        return AjaxResult.success(map);
    }

    /**
     * 根据任务ID查询挂载的表单信息
     *
     * @param taskId 任务Id
     * @return
     */
    @Override
    public AjaxResult getTaskForm(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        SysForm sysForm = sysFormService.selectSysFormById(Long.parseLong(task.getFormKey()));
        return AjaxResult.success(sysForm.getFormContent());
    }

    /**
     * 获取流程过程图
     *
     * @param processId
     * @return
     */
    @Override
    public InputStream diagram(String processId) {
        String processDefinitionId;
        // 获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        // 如果流程已经结束，则得到结束节点
        if (Objects.isNull(processInstance)) {
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();

            processDefinitionId = pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }

        // 获得活动的节点
        List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();

        List<String> highLightedFlows = new ArrayList<>();
        List<String> highLightedNodes = new ArrayList<>();
        //高亮线
        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
            if ("sequenceFlow".equals(tempActivity.getActivityType())) {
                //高亮线
                highLightedFlows.add(tempActivity.getActivityId());
            } else {
                //高亮节点
                highLightedNodes.add(tempActivity.getActivityId());
            }
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
        //获取自定义图片生成器
        ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(),
                configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
        return in;

    }

    /**
     * 获取流程执行节点
     *
     * @param flowTaskVo 流程信息
     * @return
     */
    @Override
    public AjaxResult getFlowViewer(FlowTaskVo flowTaskVo) {
        List<FlowViewerDto> flowViewerList = new ArrayList<>();
        FlowViewerDto flowViewerDto;
        // 获取任务开始节点(临时处理方式)
        List<HistoricActivityInstance> startNodeList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(flowTaskVo.getProcInsId())
                .orderByHistoricActivityInstanceStartTime()
                .asc().listPage(0, 3);
        for (HistoricActivityInstance startInstance : startNodeList) {
            if (!"sequenceFlow".equals(startInstance.getActivityType())) {
                flowViewerDto = new FlowViewerDto();
                if (!"sequenceFlow".equals(startInstance.getActivityType())) {
                    flowViewerDto.setKey(startInstance.getActivityId());
                    // 根据流程节点处理时间校验改节点是否已完成
                    flowViewerDto.setCompleted(!Objects.isNull(startInstance.getEndTime()));
                    flowViewerList.add(flowViewerDto);
                }
            }
        }
        // 历史节点
        List<HistoricActivityInstance> hisActIns = historyService.createHistoricActivityInstanceQuery()
                .executionId(flowTaskVo.getExecutionId())
                .orderByHistoricActivityInstanceStartTime()
                .asc().list();
        for (HistoricActivityInstance activityInstance : hisActIns) {
            if (!"sequenceFlow".equals(activityInstance.getActivityType())) {
                flowViewerDto = new FlowViewerDto();
                flowViewerDto.setKey(activityInstance.getActivityId());
                // 根据流程节点处理时间校验改节点是否已完成
                flowViewerDto.setCompleted(!Objects.isNull(activityInstance.getEndTime()));
                flowViewerList.add(flowViewerDto);
            }
        }
        return AjaxResult.success(flowViewerList);
    }

    /**
     * 获取流程变量
     *
     * @param taskId
     * @return
     */
    @Override
    public AjaxResult processVariables(String taskId) {
        // 流程变量
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables().finished().taskId(taskId).singleResult();
        if (Objects.nonNull(historicTaskInstance)) {
            return AjaxResult.success(historicTaskInstance.getProcessVariables());
        } else {
            Map<String, Object> variables = taskService.getVariables(taskId);
            return AjaxResult.success(variables);
        }
    }

    /**
     * 审批任务获取下一节点
     *
     * @param flowTaskVo 任务
     * @return 下一步审批节点的属性
     */
    @Override
    public AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo) {
        // Step 1. 获取当前节点并找到下一步节点
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            return AjaxResult.error("任务不存在或已被审批!");
        }
        // Step 2. 获取当前流程所有流程变量(网关节点时需要校验表达式)
        Map<String, Object> variables = taskService.getVariables(task.getId());
        List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, variables);
        if (CollectionUtils.isEmpty(nextUserTask)) {
            return AjaxResult.success("流程已完结!", null);
        }
        // Step 3. 获取下一步节点的属性
        FlowNextDto flowNextTask = getFlowAttribute(nextUserTask);
        return AjaxResult.success(flowNextTask);
    }

    /**
     * 重新发起任务申请时获取下一节点
     *
     * @param flowTaskVo 任务
     * @return 下一步审批节点的属性
     */
    @Override
    public AjaxResult getNextFlowNodeByRefill(FlowTaskVo flowTaskVo) {
        // Step 1. 获取当前节点并找到下一步节点
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        if (Objects.isNull(task)) {
            return AjaxResult.error("任务不存在或已被审批!");
        }
        // Step 2. 获取当前流程所有流程变量(网关节点时需要校验表达式)
        Map<String, Object> variables = taskService.getVariables(task.getId());
        List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, variables);
        if (CollectionUtils.isEmpty(nextUserTask)) {
            return AjaxResult.success("流程已完结!", null);
        }
        // Step 3. 获取下一步节点的属性
        FlowNextDto flowNextTask = getFlowAttribute(nextUserTask);
        // 设置任务审批人/组 ,再次发起申请则不需要选择任务接收人
        flowNextTask.setApproval(variables.get(ProcessConstants.PROCESS_APPROVAL).toString());
        List<String> list = Arrays.asList(flowNextTask.getApproval().split(","));
        if (ProcessConstants.CANDIDATE_GROUPS.equals(flowNextTask.getType())) {
            flowNextTask.setRoleList(sysRoleService.selectRoleAll()
                    .stream().filter(role -> list.contains(role.getRoleId().toString())).collect(Collectors.toList()));
        } else {
            flowNextTask.setUserList(sysUserService.selectUserList(new SysUser())
                    .stream().filter(user -> list.contains(user.getUserId().toString())).collect(Collectors.toList()));
        }
        return AjaxResult.success(flowNextTask);
    }

    /**
     * 发起流程获取下一节点
     *
     * @param flowTaskVo 任务
     * @return
     */
    @Override
    public AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo) {
        // Step 1. 查找流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(flowTaskVo.getDeployId()).singleResult();
        if (Objects.isNull(processDefinition)) {
            return AjaxResult.error("流程信息不存在!");
        }
        // Step 2. 获取下一任务节点(网关节点时需要校验表达式)
        List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasksByStart(repositoryService, processDefinition, flowTaskVo.getVariables());
        if (CollectionUtils.isEmpty(nextUserTask)) {
            return AjaxResult.error("暂未查找到下一任务,请检查流程设计是否正确!");
        }
        // Step 3. 获取下一任务节点的属性
        FlowNextDto flowNextTask = getFlowAttribute(nextUserTask);
        return AjaxResult.success(flowNextTask);
    }


    /**
     * 获取任务节点属性,包含自定义属性等
     *
     * @param nextUserTask
     */
    private FlowNextDto getFlowAttribute(List<UserTask> nextUserTask) {
        // TODO如果下一个任务有多个节点可供选择，则需要返回一个列表，并且前端需要分批选择节点任务候选者。
        // 循环遍历nextUserTask列表以确定任务节点的类型
        FlowNextDto flowNextDto = new FlowNextDto();
        for (UserTask userTask : nextUserTask) {
            MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics();
            // 如果是会签节点
            if (Objects.nonNull(multiInstance)) {
                flowNextDto.setFlowParam(multiInstance.getInputDataItem());
                flowNextDto.setType(ProcessConstants.PROCESS_MULTI_INSTANCE);
                flowNextDto.setDataType(ProcessConstants.DYNAMIC);
            } else {
                // 读取自定义节点属性以确定审批任务是否需要动态指定下一步任务接收者或组
                String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
                String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE);
                // 如果没有自定义属性则默认为动态指定下一步任务接收者 此时流程xml中应该缺少 "flowable:userType"
                if (StringUtils.isBlank(userType)) {
                    userType = ProcessConstants.ASSIGNEE;
                }
                flowNextDto.setFlowParam(ProcessConstants.PROCESS_APPROVAL);
                flowNextDto.setType(userType);
                flowNextDto.setDataType(dataType);
            }
        }
        return flowNextDto;
    }

    /**
     * 流程初始化表单
     *
     * @param deployId
     * @return
     */
    @Override
    public AjaxResult flowFormData(String deployId) {
        // 第一次申请获取初始化表单
        if (StringUtils.isNotBlank(deployId)) {
            SysForm sysForm = sysDeployFormService.selectSysDeployFormByDeployId(deployId);
            if (Objects.isNull(sysForm)) {
                return AjaxResult.error("请先配置流程表单!");
            }
            return AjaxResult.success(JSONObject.parseObject(sysForm.getFormContent()));
        } else {
            return AjaxResult.error("参数错误!");
        }
    }

    /**
     * 流程节点信息
     *
     * @param flowTaskVo 流程信息
     * @return
     */
    @Override
    public AjaxResult flowXmlAndNode(FlowTaskVo flowTaskVo) {
        try {
            List<FlowViewerDto> flowViewerList = new ArrayList<>();
            // 获取已经完成的节点
            List<HistoricActivityInstance> listFinished = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(flowTaskVo.getProcInsId())
                    .finished()
                    .list();
            // 获取代办节点
            List<HistoricActivityInstance> listUnFinished = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(flowTaskVo.getProcInsId())
                    .unfinished()
                    .list();

            // 保存需要代办的节点编号
            listUnFinished.forEach(s -> {
                FlowViewerDto flowViewerDto = new FlowViewerDto();
                flowViewerDto.setKey(s.getActivityId());
                flowViewerDto.setCompleted(false);
                flowViewerList.add(flowViewerDto);
            });

            // 过滤掉退回,驳回的节点
            for (HistoricActivityInstance finished : listFinished) {
                boolean flag = listUnFinished.stream().anyMatch(unFinished -> unFinished.getActivityId().equals(finished.getActivityId()));
                if (flag) {
                    break;
                }
                FlowViewerDto flowViewerDto = new FlowViewerDto();
                flowViewerDto.setKey(finished.getActivityId());
                flowViewerDto.setCompleted(true);
                flowViewerList.add(flowViewerDto);
            }

            Map<String, Object> result = new HashMap<>(2);
            // xmlData 数据
            ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(flowTaskVo.getDeployId()).singleResult();
            InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
            String xmlData = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            result.put("nodeData", flowViewerList);
            result.put("xmlData", xmlData);
            return AjaxResult.success(result);
        } catch (Exception e) {
            return AjaxResult.error("查询历史任务失败");
        }
    }


    /**
     * 流程节点表单
     *
     * @param taskId 流程任务编号
     * @return
     */
    @Override
    public AjaxResult flowTaskForm(String taskId) throws Exception {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 流程变量
        Map<String, Object> parameters = new HashMap<>();
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables().finished().taskId(taskId).singleResult();
        if (Objects.nonNull(historicTaskInstance)) {
            parameters = historicTaskInstance.getProcessVariables();
        } else {
            parameters = taskService.getVariables(taskId);
        }
        JSONObject oldVariables = JSONObject.parseObject(JSON.toJSONString(parameters.get("formJson")));
        List<JSONObject> oldFields = JSON.parseObject(JSON.toJSONString(oldVariables.get("widgetList")), new TypeReference<List<JSONObject>>() {
        });
        // 设置已填写的表单为禁用状态
        oldFields.forEach(oldField -> {
            JSONObject options = oldField.getJSONObject("options");
            options.put("disabled", true);
        });
        // TODO 暂时只处理用户任务上的表单
        if (StringUtils.isNotBlank(task.getFormKey())) {
            SysForm sysForm = sysFormService.selectSysFormById(Long.parseLong(task.getFormKey()));
            JSONObject data = JSONObject.parseObject(sysForm.getFormContent());
            List<JSONObject> newFields = JSON.parseObject(JSON.toJSONString(data.get("widgetList")), new TypeReference<List<JSONObject>>() {
            });
            // 表单回显时 加入子表单信息到流程变量中
            for (JSONObject newField : newFields) {
                String key = newField.getString("id");
                // 处理图片上传组件回显问题
                if ("picture-upload".equals(newField.getString("type"))
                        || "file-upload".equals(newField.getString("type"))) {
                    parameters.put(key, new ArrayList<>());
                } else {
                    parameters.put(key, null);
                }
            }
            oldFields.addAll(newFields);
        }
        oldVariables.put("widgetList", oldFields);
        parameters.put("formJson", oldVariables);
        return AjaxResult.success(parameters);
    }


    /**
     * 流程节点按钮
     *
     * @param taskId 流程任务编号
     * @return
     */
    @Override
    public AjaxResult flowButton(String taskId) throws Exception {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        List<FlowButtonDto> buttonList = FindNextNodeUtil.getButtonElement(repositoryService, task);
        return AjaxResult.success(buttonList);
    }

    /**
     * 获取流程任务信息
     *
     * @param flowTaskVo 流程任务信息
     * @return AjaxResult
     */
    public AjaxResult flowTaskInfo(FlowTaskVo flowTaskVo) {
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(flowTaskVo.getProcInsId())
                .activityId(flowTaskVo.getElementId())
                .list();
        // 退回任务后有多条数据 只取待办任务进行展示
        list.removeIf(task -> StringUtils.isNotBlank(task.getDeleteReason()));
        if (CollectionUtils.isEmpty(list)) {
            return AjaxResult.success();
        }
        if (list.size() > 1) {
            list.removeIf(task -> Objects.nonNull(task.getEndTime()));
        }
        HistoricActivityInstance histIns = list.get(0);
        FlowTaskDto flowTask = new FlowTaskDto();
        flowTask.setTaskId(histIns.getTaskId());
        flowTask.setTaskName(histIns.getActivityName());
        flowTask.setCreateTime(histIns.getStartTime());
        flowTask.setFinishTime(histIns.getEndTime());
        if (StringUtils.isNotBlank(histIns.getAssignee())) {
            if (ProcessConstants.PROCESS_INITIATOR.equals(histIns.getAssignee())) {
                // 流程发起时, 申请人节点固定设置了description和assignee 为 INITIATOR, 所以可以通过该字段来校验是否需要重新发起任务
                flowTask.setRefill(true);
            } else if (ProcessConstants.PROCESS_REVOKE.equals(histIns.getAssignee())) {
                flowTask.setRevoke(true);
            } else {
                SysUser sysUser = sysUserService.selectUserById(Long.parseLong(histIns.getAssignee()));
                flowTask.setAssigneeId(sysUser.getUserId());
                flowTask.setAssigneeName(sysUser.getNickName());
                flowTask.setDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : "");
            }
        }
        // 展示审批人员
        List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getTaskId());
        StringBuilder stringBuilder = new StringBuilder();
        linksForTask.forEach(identityLink -> {
            // 获选人,候选组/角色(多个)
            if ("candidate".equals(identityLink.getType())) {
                if (StringUtils.isNotBlank(identityLink.getUserId())) {
                    SysUser sysUser = sysUserService.selectUserById(Long.parseLong(identityLink.getUserId()));
                    stringBuilder.append(sysUser.getNickName()).append(",");
                }
                if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                    SysRole sysRole = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId()));
                    stringBuilder.append(sysRole.getRoleName()).append(",");
                }
            }
        });
        if (StringUtils.isNotBlank(stringBuilder)) {
            flowTask.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
        }

        flowTask.setDuration(histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis()));
        // 获取意见评论内容
        List<Comment> commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId());
        List<FlowCommentDto> commentDtoList = new ArrayList<>();
        commentList.forEach(comment -> {
            if (histIns.getTaskId().equals(comment.getTaskId())) {
                FlowComment flowComment = FlowComment.getFlowCommentByType(comment.getType());
                FlowCommentDto commentDto = FlowCommentDto.builder()
                        .type(comment.getType())
                        .content(comment.getFullMessage())
                        .remark(flowComment.getRemark())
                        .style(flowComment.getStyle())
                        .dateTime(comment.getTime())
                        .build();
                commentDtoList.add(commentDto);
            }
        });
        flowTask.setCommentList(commentDtoList);
        return AjaxResult.success(flowTask);
    }

    // 发送任务抄送信息
    private void sendCopyTask(FlowTaskVo taskVo, Task task) {
        if (CollectionUtils.isNotEmpty(taskVo.getCopyUser())) {
            // 流程定义信息
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(task.getProcessDefinitionId())
                    .singleResult();
            SysCopy sysCopy = new SysCopy();
            SysUser sysUser = SecurityUtils.getLoginUser().getUser();
            sysCopy.setProcInsId(task.getProcessInstanceId());
            sysCopy.setDeployId(pd.getDeploymentId());
            sysCopy.setProcName(pd.getName());
            sysCopy.setTaskId(task.getId());
            sysCopy.setTaskName(task.getName());
            sysCopy.setSendUserId(sysUser.getUserId());
            sysCopy.setSendUserName(sysUser.getNickName());
            sysCopy.setStatus(0);
            taskVo.getCopyUser().forEach(userId -> {
                sysCopy.setReceiveUserId(userId);
                sysCopyService.insertSysCopy(sysCopy);
            });
        }
    }

    /**
     * 将Object类型的数据转化成Map<String,Object>
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public Map<String, Object> obj2Map(Object obj) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            map.put(field.getName(), field.get(obj));
        }
        return map;
    }

    /**
     * 流程完成时间处理
     *
     * @param ms
     * @return
     */
    private String getDate(long ms) {

        long day = ms / (24 * 60 * 60 * 1000);
        long hour = (ms / (60 * 60 * 1000) - day * 24);
        long minute = ((ms / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long second = (ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);

        StringBuilder sb = new StringBuilder();
        if (day > 0) {
            sb.append(day).append("天");
        }
        if (hour > 0) {
            sb.append(hour).append("小时");
        }
        if (minute > 0) {
            sb.append(minute).append("分钟");
        }
        if (second > 0) {
            sb.append(second).append("秒");
        } else {
            sb.append(0).append("秒");
        }
        return sb.toString();
    }
}
